From: Carl Lerche Date: Wed, 19 Mar 2014 01:10:48 +0000 (-0700) Subject: Initial stab at integration tests X-Git-Tag: archive/raspbian/0.35.0-2+rpi1~3^2^2^2^2^2^2^2~1129^2~9 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/success//%22http:/www.example.com/cgi/success/?a=commitdiff_plain;h=8c005bb48dd13a68e9965a88a5bd97dd5acdab57;p=cargo.git Initial stab at integration tests At the same time, we started adding a generic error handling concept to Cargo. The idea is that every Result gets converted to a CargoError, which includes all the information that Cargo needs to print out a friendly human error message and exit gracefully. --- diff --git a/Makefile b/Makefile index af30c5eb0..663e314de 100644 --- a/Makefile +++ b/Makefile @@ -42,11 +42,22 @@ libcargo: $(LIBCARGO) $(BIN_TARGETS): target/%: src/bin/%.rs $(HAMMER) $(TOML) $(LIBCARGO) $(RUSTC) $(RUSTC_FLAGS) $(DEPS) -Ltarget --out-dir target $< -test: - echo "testing" +# === Tests + +TEST_SRC = $(wildcard tests/*.rs) +TEST_DEPS = $(DEPS) -L libs/hamcrest-rust/target + +tests/tests: $(BIN_TARGETS) $(HAMCREST) $(TEST_SRC) + $(RUSTC) --test --crate-type=lib $(TEST_DEPS) -Ltarget --out-dir tests tests/tests.rs + +test-integration: tests/tests + tests/tests + +test: test-integration clean: rm -rf target + rm -f tests/tests distclean: clean cd libs/hamcrest-rust && make clean @@ -54,7 +65,8 @@ distclean: clean cd libs/rust-toml && make clean # Setup phony tasks -.PHONY: all clean distclean test libcargo +.PHONY: all clean distclean test test-integration libcargo # Disable unnecessary built-in rules .SUFFIXES: + diff --git a/src/bin/cargo-read-manifest.rs b/src/bin/cargo-read-manifest.rs index e8e224e11..e7d9fc0d1 100644 --- a/src/bin/cargo-read-manifest.rs +++ b/src/bin/cargo-read-manifest.rs @@ -9,7 +9,7 @@ use hammer::{FlagDecoder,FlagConfig,FlagConfiguration}; use serialize::{Decoder,Decodable}; use serialize::json::Encoder; use toml::from_toml; -use cargo::{Manifest,LibTarget,ExecTarget,Project}; +use cargo::{Manifest,LibTarget,ExecTarget,Project,CargoResult,CargoError,ToCargoError}; use std::path::Path; #[deriving(Decodable,Encodable,Eq,Clone,Ord)] @@ -41,21 +41,32 @@ impl FlagConfig for ReadManifestFlags { } fn main() { + match execute() { + Err(e) => { + println!("{}", e.message); + // TODO: Exit with error code + }, + _ => return + } +} + +fn execute() -> CargoResult<()> { let mut decoder = FlagDecoder::new::(std::os::args().tail()); let flags: ReadManifestFlags = Decodable::decode(&mut decoder); if decoder.error.is_some() { - fail!("Error: {}", decoder.error.unwrap()); + return Err(CargoError::new(decoder.error.unwrap(), 1)); } - let root = toml::parse_from_file(flags.manifest_path).unwrap(); + let manifest_path = flags.manifest_path; + let root = try!(toml::parse_from_file(manifest_path.clone()).to_cargo_error(format!("Couldn't parse Toml file: {}", manifest_path), 1)); let toml_manifest = from_toml::(root.clone()); let (lib, bin) = normalize(&toml_manifest.lib, &toml_manifest.bin); let manifest = Manifest{ - root: Path::new(flags.manifest_path).dirname_str().unwrap().to_owned(), + root: try!(Path::new(manifest_path.clone()).dirname_str().to_cargo_error(format!("Could not get dirname from {}", manifest_path), 1)).to_owned(), project: toml_manifest.project, lib: lib, bin: bin @@ -64,6 +75,8 @@ fn main() { let encoded: ~str = Encoder::str_encode(&manifest); println!("{}", encoded); + + Ok(()) } fn normalize(lib: &Option<~[SerializedLibTarget]>, bin: &Option<~[SerializedExecTarget]>) -> (~[LibTarget], ~[ExecTarget]) { diff --git a/src/bin/cargo-rustc.rs b/src/bin/cargo-rustc.rs index 70c62a86b..4e7778655 100644 --- a/src/bin/cargo-rustc.rs +++ b/src/bin/cargo-rustc.rs @@ -10,7 +10,7 @@ use std::io::process::{Process,ProcessConfig,InheritFd}; use serialize::json; use serialize::Decodable; use std::path::Path; -use cargo::Manifest; +use cargo::{Manifest,CargoResult,ToCargoError}; /** cargo-rustc -- ...args @@ -19,10 +19,20 @@ use cargo::Manifest; */ fn main() { + match execute() { + Err(e) => { + println!("{}", e.message); + // TODO: Exit with error code + }, + _ => return + } +} + +fn execute() -> CargoResult<()> { let mut reader = io::stdin(); - let input = reader.read_to_str().unwrap(); + let input = try!(reader.read_to_str().to_cargo_error(~"Cannot read stdin to a string", 1)); - let json = json::from_str(input).unwrap(); + let json = try!(json::from_str(input).to_cargo_error(format!("Cannot parse json: {}", input), 1)); let mut decoder = json::Decoder::new(json); let manifest: Manifest = Decodable::decode(&mut decoder); @@ -51,15 +61,17 @@ fn main() { config.program = "rustc"; config.args = args.as_slice(); - let mut p = Process::configure(config).unwrap(); + let mut p = try!(Process::configure(config).to_cargo_error(format!("Could not start process: rustc {}", args.as_slice()), 1)); let status = p.wait(); if status != std::io::process::ExitStatus(0) { fail!("Failed to execute") } + + Ok(()) } fn join(path: &Path, part: ~str) -> ~str { - path.join(part).as_str().unwrap().to_owned() + format!("{}", path.join(part).display()) } diff --git a/src/cargo.rs b/src/cargo.rs index 684b8c920..13a2ea5a8 100644 --- a/src/cargo.rs +++ b/src/cargo.rs @@ -2,6 +2,8 @@ extern crate serialize; use serialize::{Decoder}; +use std::fmt; +use std::fmt::{Show,Formatter}; #[deriving(Decodable,Encodable,Eq,Clone,Ord)] pub struct Manifest { @@ -32,3 +34,44 @@ pub struct Project { version: ~str, authors: ~[~str] } + +pub type CargoResult = Result; + +pub struct CargoError { + message: ~str, + exit_code: uint +} + +impl CargoError { + pub fn new(message: ~str, exit_code: uint) -> CargoError { + CargoError { message: message, exit_code: exit_code } + } +} + +impl Show for CargoError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f.buf, "{}", self.message) + } +} + +pub trait ToCargoError { + fn to_cargo_error(self, message: ~str, exit_code: uint) -> Result; +} + +impl ToCargoError for Result { + fn to_cargo_error(self, message: ~str, exit_code: uint) -> Result { + match self { + Err(_) => Err(CargoError{ message: message, exit_code: exit_code }), + Ok(val) => Ok(val) + } + } +} + +impl ToCargoError for Option { + fn to_cargo_error(self, message: ~str, exit_code: uint) -> CargoResult { + match self { + None => Err(CargoError{ message: message, exit_code: exit_code }), + Some(val) => Ok(val) + } + } +} diff --git a/tests/support.rs b/tests/support.rs new file mode 100644 index 000000000..293a28d1e --- /dev/null +++ b/tests/support.rs @@ -0,0 +1,118 @@ +// use std::io::fs::{mkdir_recursive,rmdir_recursive}; +use std::io::fs; +use std::os::tmpdir; +use std::path::{Path}; + +static CARGO_INTEGRATION_TEST_DIR : &'static str = "cargo-integration-tests"; +static MKDIR_PERM : u32 = 0o755; + +#[deriving(Eq,Clone)] +struct FileBuilder { + path: Path, + body: ~str +} + +impl FileBuilder { + pub fn new(path: Path, body: &str) -> FileBuilder { + FileBuilder { path: path, body: body.to_owned() } + } + + fn mk(&self) -> Result<(), ~str> { + try!(mkdir_recursive(&self.dirname())); + + let mut file = try!( + fs::File::create(&self.path) + .with_err_msg(format!("Could not create file; path={}", self.path.display()))); + + file.write_str(self.body.as_slice()) + .with_err_msg(format!("Could not write to file; path={}", self.path.display())) + } + + fn dirname(&self) -> Path { + Path::new(self.path.dirname()) + } +} + +#[deriving(Eq,Clone)] +struct ProjectBuilder { + name: ~str, + root: Path, + files: ~[FileBuilder] +} + +impl ProjectBuilder { + pub fn new(name: &str, root: Path) -> ProjectBuilder { + ProjectBuilder { + name: name.to_owned(), + root: root, + files: ~[] + } + } + + pub fn file(mut self, path: &str, body: &str) -> ProjectBuilder { + self.files.push(FileBuilder::new(self.root.join(path), body)); + self + } + + pub fn build(self) { + match self.build_with_result() { + Err(e) => fail!(e), + _ => return + } + } + + pub fn build_with_result(self) -> Result<(), ~str> { + // First, clean the directory if it already exists + try!(self.rm_root()); + + // Create the empty directory + try!(mkdir_recursive(&self.root)); + + for file in self.files.iter() { + try!(file.mk()); + } + + println!("{}", self.root.display()); + println!("{:?}", self); + Ok(()) + } + + fn rm_root(&self) -> Result<(), ~str> { + if self.root.exists() { + rmdir_recursive(&self.root) + } + else { + Ok(()) + } + } +} + +// Generates a project layout +pub fn project(name: &str) -> ProjectBuilder { + ProjectBuilder::new(name, tmpdir().join(CARGO_INTEGRATION_TEST_DIR)) +} + +// === Helpers === + +pub fn mkdir_recursive(path: &Path) -> Result<(), ~str> { + fs::mkdir_recursive(path, MKDIR_PERM) + .with_err_msg(format!("could not create directory; path={}", path.display())) +} + +pub fn rmdir_recursive(path: &Path) -> Result<(), ~str> { + fs::rmdir_recursive(path) + .with_err_msg(format!("could not rm directory; path={}", path.display())) +} + +trait ErrMsg { + fn with_err_msg(self, val: ~str) -> Result; +} + +impl ErrMsg for Result { + fn with_err_msg(self, val: ~str) -> Result { + match self { + Ok(val) => Ok(val), + Err(_) => Err(val) + } + } +} diff --git a/tests/test_cargo_compile.rs b/tests/test_cargo_compile.rs new file mode 100644 index 000000000..70ff54252 --- /dev/null +++ b/tests/test_cargo_compile.rs @@ -0,0 +1,33 @@ +use support::project; + +fn setup() { + +} + +test!(cargo_compile_with_explicit_manifest_path { + project("foo") + .file("Cargo.toml", r#" + [project] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [[lib]] + + name = "foo" + "#) + .file("src/foo.rs", r#" + fn main() { + println!("i am foo"); + }"#) + .build(); + + fail!("not implemented"); + // 1) Setup project + // 2) Run cargo-compile --manifest-path /tmp/bar/zomg + // 3) assertThat(target/foo) exists assertThat("target/foo", isCompiledBin()) + // 4) Run target/foo, assert that output is ass expected (foo.rs == println!("i am foo")) +}) + +// test!(compiling_project_with_invalid_manifest) diff --git a/tests/tests.rs b/tests/tests.rs new file mode 100644 index 000000000..6eea1cf89 --- /dev/null +++ b/tests/tests.rs @@ -0,0 +1,16 @@ +#[feature(macro_rules)]; + +extern crate cargo; + +macro_rules! test( + ($name:ident $expr:expr) => ( + #[test] + fn $name() { + setup(); + $expr; + } + ) +) + +mod support; +mod test_cargo_compile;